package com.heima.search.service.impl;

import com.alibaba.nacos.client.naming.utils.CollectionUtils;
import com.heima.common.threadlocal.UserThreadLocalUtils;
import com.heima.common.util.JsonUtils;
import com.heima.feign.article.ArticleFilent;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.media.dtos.WmNewsContentDto;
import com.heima.model.search.dtos.UserSearchDto;
import com.heima.search.service.ApUserSearchService;
import com.heima.search.service.SearchHistoryService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.util.QueryBuilder;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.naming.directory.SearchResult;
import javax.swing.text.Highlighter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class ApUserSearchServiceImpl implements ApUserSearchService {

    private final static String INDEX_NAME = "app_info_article";

    @Autowired
    private RestHighLevelClient client;

    @Override
    public void onArticleCreate(long articleId) {
        // 因为消息是异步的，访问查询article查不到，sleep
        try{
            Thread.sleep(7000);
        }catch(Exception e) {
            e.printStackTrace();
        }
        insertToES(articleId);
    }

    /**
     * test调用批量保存
     * @param articleDtoList
     */
    @Override
    public void importArticle(List<ArticleDto> articleDtoList) {
        if(CollectionUtils.isEmpty(articleDtoList)){
            return;
        }
        BulkRequest bulkRequest = new BulkRequest();
        for (ArticleDto articleDto : articleDtoList) {
            Map<String,Object> map = buildEsArticle(articleDto);
            String string = JsonUtils.toString(map);
            //  构造创建索引请求对象
            IndexRequest request = new IndexRequest(INDEX_NAME).id(articleDto.getId().toString());
            //设置索引数据
            request.source(string, XContentType.JSON);
            //添加批量对象
            bulkRequest.add(request);
            if(bulkRequest.numberOfActions() > 0){
                try {
                    client.bulk(bulkRequest, RequestOptions.DEFAULT);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    private Map<String,Object> buildEsArticle(ArticleDto articleDto) {
        Map<String,Object> map = new HashMap<>();
        map.put("id",articleDto.getId());
        map.put("title",articleDto.getTitle());
        map.put("layout",articleDto.getLayout());
        map.put("publishTime",articleDto.getPublishTime());
        map.put("channelId",articleDto.getChannelId());
        map.put("images",articleDto.getImages());
        map.put("authorId",articleDto.getAuthorId());
        map.put("authorName",articleDto.getAuthorName());
        map.put("content", JsonUtils.toString(WmNewsContentDto.getText(articleDto.getContent())));
        map.put("staticUrl",articleDto.getStaticUrl());
        map.put("labels", articleDto.getLabels());
        return map;
    }


    @Autowired
    private SearchHistoryService historyService;


    @Override
    public List<ArticleDto> search(UserSearchDto dto) {
        SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        boolQuery.must(QueryBuilders.multiMatchQuery(dto.getSearchWords(),"title","content","labels"));
        if(dto.getMinBehotTime() != null){
            boolQuery.filter(QueryBuilders.rangeQuery("publishTime").lt(dto.getMinBehotTime()));
        }
        searchRequest.source().query(boolQuery);
        //分页
        searchRequest.source().from(dto.getFromIndex());
        searchRequest.source().size(dto.getPageSize());
        //排序
        searchRequest.source().sort("publishTime", SortOrder.DESC);
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<em>");
        highlightBuilder.field("title");
        highlightBuilder.postTags("<em/>");
        searchRequest.source().highlighter(highlightBuilder);
        List<ArticleDto> articleDtoList = new ArrayList<>();
        try {
            SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits hits = search.getHits();
            for (SearchHit hit : hits.getHits()) {
                ArticleDto articleDto = JsonUtils.toBean(hit.getSourceAsString(), ArticleDto.class);
                //处理高亮
                if(hit.getHighlightFields() != null && hit.getHighlightFields().size() > 0){

                    Text[] titles = hit.getHighlightFields().get("title").getFragments();
                    String title = StringUtils.join(titles);
                    articleDto.setTitle(String.valueOf(title));
                }
                articleDtoList.add(articleDto);

            }
        } catch (IOException e) {
            log.error("高亮设置失败");
            throw new RuntimeException(e);
        }
        if(!CollectionUtils.isEmpty(articleDtoList)){
            // 异步的去保存搜索的历史记录
            historyService.asyncSaveSearchHistory(UserThreadLocalUtils.getUserId(), dto.getEquipmentId(), dto.getSearchWords());
        }

        return articleDtoList;
    }

    @Autowired
    private ArticleFilent articleFilent;
    /**
     * search收到自媒体文章上下架消息的回调
     * */
    @Override
    public void upOrDown(Map<String, Long> map) {
        Long enable = map.get("enable");
        Long articleId = map.get("articleId");
        try {
            Thread.sleep(7000);
        }catch (Exception e){
            e.printStackTrace();
        }
        if (enable == 1){
            insertToES(articleId);
        }else {
            deleteFromES(articleId);
        }
    }

    private void deleteFromES(Long articleId) {
        DeleteRequest deleteRequest = new DeleteRequest(INDEX_NAME).id("" + articleId);
        try{
            client.delete(deleteRequest, RequestOptions.DEFAULT);
        }catch(Exception e){
            e.printStackTrace();
            log.error("删除es的index异常，id：{}，异常:{}", articleId, ExceptionUtils.getStackTrace(e));
        }
    }

    private void insertToES(Long articleId) {
        //insert
        IndexRequest indexRequest = new IndexRequest(INDEX_NAME).id(""+articleId);
        ArticleDto articleDto =  articleFilent.findById(articleId);
        if (articleDto == null){
            log.error("根据articleId查询不到article数据,articleId:{}", articleId);
            return;
        }
        Map<String,Object> articleMap = buildEsArticle(articleDto);
        indexRequest.source(articleMap,XContentType.JSON);
        try {
            client.index(indexRequest,RequestOptions.DEFAULT);

        }catch (Exception e){
            e.printStackTrace();
            log.error("保存es的index异常:{}", ExceptionUtils.getStackTrace(e));

        }
    }
}
